package org.example.big_data.interceptors;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.big_data.pojo.Result;
import org.example.big_data.utils.JwtUtil;
import org.example.big_data.utils.ThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Map;

@Component
public class LoginInterceptor implements HandlerInterceptor {
    //redis核心类
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");//处理前端的HTTP访问请求，获取请求头中的Authorization字段，其中的值就是token
        try {
            //解析并验证token如果是无效token则会抛出一续航因此可以用try -catch蓝判断
            Map<String,Object> claims = JwtUtil.parseToken(token);  ///解析token并取出载荷中的claims

            /**
             * 1.1 从redis中取出token，判断是否存在，存在则放行，不存在则拦截
             */
            ValueOperations<String,String> operations = stringRedisTemplate.opsForValue();  //验证
            String redisToken = operations.get(token);  //根据前端传入的token（request.getHeader("Authorization");）查询，若查询的到并且值相同
            if (!StringUtils.hasLength(redisToken)) {  //redis中没有token，即token无效

                //直接throw
                throw new RuntimeException("登陆凭证已过期或无效，请重新登录");  //抛出这个异常后，catch拦截下来，然后在catch中处理，生成自定义的HTTP的异常响应
            }

            /**
             * 当用户更改密码后，redis中的token会失效，因此需要重新生成一个token，存入redis中
             */


            //2.11 使用threadlocal工具类将在在载荷的数据存入线程变量中,get取出时就是已经解析过的载荷Map字典
            ThreadLocalUtil.set(claims);

            //在拦截器的处理函数中返回true代表放行，即各个controller中的处理方法可以执行
            return true;


        } catch (Exception e) {  //捕获token不通过时抛出的二RunException
            Result<?> result = Result.error("登陆凭证已过期或无效，请重新登录");
            response.setStatus(401);  // 设置HTTP状态码为401
            /**
             * 嗨嗨嗨！自己研究的响应401同时返回自定义的异常信息，获取result对象，如果你不手动设置响应体的话，那么http响应401的时候，就不会生成任何响应体，单纯调用result.error没有用
             * 用response的方法，手动设置响应体的内容类型，将Result对象序列化为JSON并写入响应体
             */
            response.setContentType("application/json;charset=UTF-8");  // 设置响应体的内容类型
            response.getWriter().write(new ObjectMapper().writeValueAsString(result));  // 将Result对象序列化为JSON并写入响应体
            return false;  // 不放行
        }
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadLocalUtil.remove();  //2.11小细节，线程结束后（即controller执行完）要清除线程变量
    }
}



